
/**
 * \ingroup rpmesh
 * \page rpmeshoverview RpMesh Overview
 *
 * \par Requirements
 * \li \b Headers: rwcore.h, rpworld.h
 * \li \b Libraries: rwcore, rpworld
 * \li \b Plugin \b attachments: \ref RpWorldPluginAttach
 *
 * \subsection meshoverview Overview
 *
 * Meshes are a caching system designed to speed up rendering.
 *
 * To make efficient use of hardware acceleration, RenderWare Graphics groups your
 * model geometry into Meshes when the Geometry object is loaded and/or unlocked.
 * Meshes are generated by sorting the model geometry by Material to reduce repeated
 * uploads of the same texture data and Tristripping is also performed at the this level.
 *
 * The RpMesh API concerns itself primarily with the tristripping process. It is possible
 * to attach your own callback function to use your own tristripping algorithms if required.
 *
 * \note Manipulating geometry data at run-time can affect overall performance of your application
 * as the Meshes need to be regenerated when such changes are made. For small numbers of changes,
 * the effect should be negligible, but this depends heavily on the underlying architecture
 * of the hardware.
 *
 * Further information is available in the \e Dynamic \e Models chapter of the User Guide.
*
*/

/****************************************************************************
 Includes
 */

#include <stdlib.h>
#include <string.h>

#include <rwcore.h>

#include "bamatlst.h"
#include "baworld.h"
#include "bamesh.h"
#include "bameshop.h"

/****************************************************************************
 Local Types
 */

typedef struct binMeshHeader binMeshHeader;
struct binMeshHeader
{
    RwUInt32            flags;
    RwUInt32            numMeshes;
    RwUInt32            totalIndicesInMesh;
};

typedef struct binMesh binMesh;
struct binMesh
{
    RwUInt32            numIndices;
    RwInt32             matIndex;
};

typedef struct RpMeshStatic RpMeshStatic;
struct RpMeshStatic
{
    RwFreeList         *BuildMeshFreeList;
};

#define RWINDEXBUFFERSIZE 256
typedef RwUInt32    RpIndexBuffer[RWINDEXBUFFERSIZE];

/****************************************************************************
 Globals (across program)
 */

RwModuleInfo        meshModule;

/****************************************************************************
 Local (static) Globals
 */

static RpMeshStatic MeshStatic = {
    (RwFreeList *) NULL         /* BuildMeshFreeList */
};

static void
MeshFreeListsDestroy(void)
{
    RWFUNCTION(RWSTRING("MeshFreeListsDestroy"));

    if (NULL != MeshStatic.BuildMeshFreeList)
    {
        RwFreeListDestroy(MeshStatic.BuildMeshFreeList);
        MeshStatic.BuildMeshFreeList = (RwFreeList *) NULL;
    }

    RWRETURNVOID();
}

static              RwBool
MeshFreeListsCreate(void)
{
    RwBool              result;

    RWFUNCTION(RWSTRING("MeshFreeListsCreate"));

    MeshStatic.BuildMeshFreeList =
        RwFreeListCreate(sizeof(RpBuildMesh), 50, sizeof(RwUInt32),
                         rwMEMHINTDUR_GLOBAL | rwID_MESHMODULE);
    result = (NULL != MeshStatic.BuildMeshFreeList);

    RWRETURN(result);
}

/****************************************************************************/
/* Mesh header create/destroy functions */

void
_rpMeshHeaderDestroy(RpMeshHeader * meshHeader)
{
    RWFUNCTION(RWSTRING("_rpMeshHeaderDestroy"));

    RWASSERT((RpMeshHeader *)NULL != meshHeader);

    RwFree(meshHeader);

    meshHeader = (RpMeshHeader *)NULL;

    RWRETURNVOID();
}

RpMeshHeader *
_rpMeshHeaderCreate(RwUInt32 size)
{
    RpMeshHeader * meshHeader;

    RWFUNCTION(RWSTRING("_rpMeshHeaderCreate"));

    meshHeader = (RpMeshHeader *) RwMalloc(size,
        rwMEMHINTDUR_EVENT | rwID_MESHMODULE);

    RWRETURN(meshHeader);
}

/****************************************************************************
 _rpMeshClose

 On entry   : instance, offset, size
 On exit    : instance pointer on success
 */

void *
_rpMeshClose(void *instance,
             RwInt32 __RWUNUSED__ offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION(RWSTRING("_rpMeshClose"));

    /* One less module instance */
    meshModule.numInstances--;

    if (0 == meshModule.numInstances)
    {
        MeshFreeListsDestroy();
    }

    /* Success !! */
    RWRETURN(instance);
}

/****************************************************************************
 _rpMeshOpen

 On entry   : instance, offset, size
 On exit    : instance pointer on success
 */

void *
_rpMeshOpen(void *instance,
            RwInt32 __RWUNUSED__ offset, RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION(RWSTRING("_rpMeshOpen"));

    /* Cache the global data offset (same for all instances) */
    meshModule.globalsOffset = offset;

    if (0 == meshModule.numInstances)
    {
        if (!MeshFreeListsCreate())
        {
            MeshFreeListsDestroy();
            instance = NULL;
            RWRETURN(instance);
        }
    }

    /* Do the setup for this instance */
    RPMESHGLOBAL(nextSerialNum) = 1;

    /* One more module instance */
    meshModule.numInstances++;

    /* Set up LUTs for: RwPrimitiveType <--> rpMESHHEADER[primtype] */
    RPMESHGLOBAL(meshFlagsToPrimType)[0 /*rpMESHHEADERTRILIST */ ] =
        rwPRIMTYPETRILIST;
    RPMESHGLOBAL(meshFlagsToPrimType)[rpMESHHEADERTRISTRIP] =
        rwPRIMTYPETRISTRIP;
    RPMESHGLOBAL(meshFlagsToPrimType)[rpMESHHEADERTRIFAN] =
        rwPRIMTYPETRIFAN;
    RPMESHGLOBAL(meshFlagsToPrimType)[rpMESHHEADERLINELIST] =
        rwPRIMTYPELINELIST;
    RPMESHGLOBAL(meshFlagsToPrimType)[rpMESHHEADERPOLYLINE] =
        rwPRIMTYPEPOLYLINE;
    RPMESHGLOBAL(meshFlagsToPrimType)[rpMESHHEADERPOINTLIST] =
        rwPRIMTYPEPOINTLIST;
    RPMESHGLOBAL(primTypeToMeshFlags)[rwPRIMTYPELINELIST] =
        rpMESHHEADERLINELIST;
    RPMESHGLOBAL(primTypeToMeshFlags)[rwPRIMTYPEPOLYLINE] =
        rpMESHHEADERPOLYLINE;
    RPMESHGLOBAL(primTypeToMeshFlags)[rwPRIMTYPETRILIST] =
        0 /*rpMESHHEADERTRILIST */ ;
    RPMESHGLOBAL(primTypeToMeshFlags)[rwPRIMTYPETRISTRIP] =
        rpMESHHEADERTRISTRIP;
    RPMESHGLOBAL(primTypeToMeshFlags)[rwPRIMTYPETRIFAN] =
        rpMESHHEADERTRIFAN;
    RPMESHGLOBAL(primTypeToMeshFlags)[rwPRIMTYPEPOINTLIST] =
        rpMESHHEADERPOINTLIST;

    /* Success */
    RWRETURN(instance);
}

/****************************************************************************
 _rpBuildMeshCreate

 On entry   : A hint as to how many triangles should be.  Prevents reallocs later.
 On exit    : Pointer to new mesh on success
 */

RpBuildMesh *
_rpBuildMeshCreate(RwUInt32 bufferSize)
{
    RpBuildMesh        *mesh;

    RWFUNCTION(RWSTRING("_rpBuildMeshCreate"));

    mesh =
        (RpBuildMesh *) RwFreeListAlloc(MeshStatic.BuildMeshFreeList,
                            rwMEMHINTDUR_EVENT | rwID_MESHMODULE);

    if (mesh)
    {
        mesh->numTriangles = 0; /* Mark the end of the list */

        /* Provided hint as to how big? */
        if (bufferSize)
        {
            RwUInt32            size = (bufferSize *
                                        sizeof(RpBuildMeshTriangle));
            mesh->meshTriangles =
                (RpBuildMeshTriangle *) RwMalloc(size,
                    rwMEMHINTDUR_EVENT | rwID_MESHMODULE |
                    rwMEMHINTFLAG_RESIZABLE);

            if (!mesh->meshTriangles)
            {
                /* Can't allocate the number of triangles we wanted,
                 * so fail to call */
                RwFreeListFree(MeshStatic.BuildMeshFreeList, mesh);
                RWERROR((E_RW_NOMEM, size));
                RWRETURN((RpBuildMesh *) NULL);
            }
            mesh->triangleBufferSize = bufferSize;
        }
        else
        {
            mesh->meshTriangles = (RpBuildMeshTriangle *) NULL;
            mesh->triangleBufferSize = 0;
        }

        RWRETURN(mesh);
    }

    RWERROR((E_RW_NOMEM, sizeof(RpBuildMesh)));
    RWRETURN((RpBuildMesh *) NULL);
}

/****************************************************************************
 _rpBuildMeshDestroy

 On entry   : Mesh to detroy
 On exit    : TRUE on success
 */

RwBool
_rpBuildMeshDestroy(RpBuildMesh * mesh)
{
    RWFUNCTION(RWSTRING("_rpBuildMeshDestroy"));
    RWASSERT(mesh);

    if (mesh->meshTriangles)
    {
        RwFree(mesh->meshTriangles);
        mesh->meshTriangles = (RpBuildMeshTriangle *) NULL;
    }

    RwFreeListFree(MeshStatic.BuildMeshFreeList, mesh);
    mesh = (RpBuildMesh *) NULL;

    RWRETURN(TRUE);
}

/****************************************************************************
 _rpMeshDestroy

 On entry   : Mesh to detroy
 On exit    : TRUE on success
 */

RwBool
_rpMeshDestroy(RpMeshHeader * mesh)
{
    RWFUNCTION(RWSTRING("_rpMeshDestroy"));
    RWASSERT(mesh);

    /* are we looking at a null mesh   */
    /* header if none of these are set */
    if (mesh->flags ||
        mesh->numMeshes ||
        mesh->serialNum ||
        mesh->totalIndicesInMesh || mesh->firstMeshOffset)
    {
        _rpMeshHeaderDestroy(mesh);
    }
    RWRETURN(TRUE);
}

/****************************************************************************
 rpMeshAddTriangle

 On entry   : Mesh to add to
            : Material that triangle uses
            : Vertex indices
            : Original triangle index
 On exit    : New mesh list on success, or NULL on failure
 */

RpBuildMesh *
_rpBuildMeshAddTriangle(RpBuildMesh * mesh, RpMaterial * material,
                        RwInt32 vert1, RwInt32 vert2, RwInt32 vert3,
                        RwUInt16 matIndex, RwUInt16 textureIndex,
                        RwUInt16 rasterIndex, RwUInt16 pipelineIndex)
{
    RWFUNCTION(RWSTRING("_rpBuildMeshAddTriangle"));
    RWASSERT(mesh);

    /* Run out of space? */
    if (mesh->numTriangles >= mesh->triangleBufferSize)
    {
        RpBuildMeshTriangle *newMeshTriangles;
        RwUInt32            size = sizeof(RpBuildMeshTriangle) *
            (mesh->numTriangles + 1);

        if (mesh->numTriangles)
        {
            /* Realloc */
            newMeshTriangles = (RpBuildMeshTriangle *)
                RwRealloc(mesh->meshTriangles, size,
                    rwMEMHINTDUR_EVENT | rwID_MESHMODULE |
                    rwMEMHINTFLAG_RESIZABLE);
        }
        else
        {
            /* Malloc */
            newMeshTriangles = (RpBuildMeshTriangle *) RwMalloc(size,
                                rwMEMHINTDUR_EVENT | rwID_MESHMODULE |
                                rwMEMHINTFLAG_RESIZABLE);
        }

        if (!newMeshTriangles)
        {
            RWERROR((E_RW_NOMEM, size));
            RWRETURN((RpBuildMesh *) NULL);
        }

        mesh->meshTriangles = newMeshTriangles;
        mesh->triangleBufferSize = mesh->numTriangles + 1;
    }

    /* Chuck the new triangle in the list */
    mesh->meshTriangles[mesh->numTriangles].material = material;
    mesh->meshTriangles[mesh->numTriangles].vertIndex[0] =
        (RwInt16) vert1;
    mesh->meshTriangles[mesh->numTriangles].vertIndex[1] =
        (RwInt16) vert2;
    mesh->meshTriangles[mesh->numTriangles].vertIndex[2] =
        (RwInt16) vert3;
    mesh->meshTriangles[mesh->numTriangles].matIndex = matIndex;
    mesh->meshTriangles[mesh->numTriangles].textureIndex = textureIndex;
    mesh->meshTriangles[mesh->numTriangles].rasterIndex = rasterIndex;
    mesh->meshTriangles[mesh->numTriangles].pipelineIndex = pipelineIndex;
    mesh->numTriangles++;

    RWRETURN(mesh);
}

#if (defined(RWDEBUG) || defined(RWSUPPRESSINLINE))

/**
 * \ingroup rpmesh
 * \ref RpMeshHeaderGetPrimType is used to extract the primitive type
 * from a mesh header's flags.
 *
 * Note that this function is used for debug purposes only and, for
 * efficiency, is available as a macro for final release versions of an
 * application.
 *
 * The world plugin must be attached before using this function.
 *
 * \param meshHeader  Pointer to the mesh header.
 *
 * \return Returns the \ref RwPrimitiveType of the mesh header.
 *
 * \see RpMeshHeaderSetPrimType
 *
 */
RwPrimitiveType
RpMeshHeaderGetPrimType(RpMeshHeader * meshHeader)
{
    RwPrimitiveType     result;
    RwUInt32            type;

    RWAPIFUNCTION(RWSTRING("RpMeshHeaderGetPrimType"));

    RWASSERT(NULL != meshHeader);
    /* Check the prim types still fit in a byte (if not then
     * we need to change our global LUT to non-RwUInt8) */
    RWASSERT(((RwUInt32) rwPRIMTYPEOR) <= 255);

    type = meshHeader->flags & rpMESHHEADERPRIMMASK;
    RWASSERT(type <= rpMESHHEADERPOINTLIST);

    result =
        (RwPrimitiveType) (RPMESHGLOBAL(meshFlagsToPrimType)[type]);

    RWRETURN(result);
}

/**
 * \ingroup rpmesh
 * \ref RpMeshHeaderSetPrimType is used to change the flags of a mesh
 * header to determine its primitive type.
 *
 * Note that this function is used for debug purposes only and, for
 * efficiency, is available as a macro for final release versions of an
 * application.
 *
 * The world plugin must be attached before using this function.
 *
 * \param meshHeader  Pointer to the mesh header.
 * \param primType    \ref RwPrimitiveType value to set.
 *
 * \return Returns a pointer to the mesh header on success, otherwise NULL
 *
 * \see RpMeshHeaderGetPrimType
 */
RpMeshHeader *
RpMeshHeaderSetPrimType(RpMeshHeader * meshHeader,
                        RwPrimitiveType primType)
{
    RWAPIFUNCTION(RWSTRING("RpMeshHeaderSetPrimType"));

    RWASSERT(NULL != meshHeader);
    /* Check the mesh header prim types still fit in a byte
     * (if not then we need to change rpMESHHEADERPRIMMASK
     * and the type of our global LUT) */
    RWASSERT(((RwUInt32) rpMESHHEADERPRIMTYPEOR) <= 255);

    meshHeader->flags =
        (meshHeader->flags & ~rpMESHHEADERPRIMMASK) |
        (rpMESHHEADERPRIMMASK &
         RPMESHGLOBAL(primTypeToMeshFlags)[primType &
                                           rpMESHHEADERPRIMMASK]);

    RWRETURN(meshHeader);
}

#endif /* (defined(RWDEBUG) || defined(RWSUPPRESSINLINE)) */

/****************************************************************************
 _rpMeshHeaderForAllMeshes

 On entry   : Mesh to enumerate, callback function, user data
 On exit    : Mehs pointer on succes, NULL on failure
 */

RpMeshHeader *
_rpMeshHeaderForAllMeshes(RpMeshHeader * meshHeader,
                          RpMeshCallBack fpCallBack, void *pData)
{
    RwInt32             numMeshes;
    RpMesh             *mesh;

    RWFUNCTION(RWSTRING("_rpMeshHeaderForAllMeshes"));
    RWASSERT(meshModule.numInstances);
    RWASSERT(meshHeader);
    RWASSERT(fpCallBack);

    numMeshes = meshHeader->numMeshes;
    mesh = ((RpMesh *) ((RwUInt8 *) (meshHeader + 1) +
                        meshHeader->firstMeshOffset));

    while (numMeshes--)
    {
        if (!fpCallBack(mesh, meshHeader, pData))
        {
            /* Early out */
            RWRETURN(meshHeader);
        }

        /* Set up for next mesh */
        mesh++;
    }

    RWRETURN(meshHeader);
}

/****************************************************************************
 _rpMeshWrite

 Writes a mesh to the given stream

 On entry   : Mesh to write
            : Object (geometry or world sector)
            : Stream to write the mesh to
            : Material list meshes materials are in
 On exit    : Stream pointer on success
 */
RwStream *
_rpMeshWrite(const RpMeshHeader *meshHeader,
             const void *object,
             RwStream *stream,
             const RpMaterialList *matList)
{
    binMeshHeader   bmh;
    RwUInt32        numMeshes;
    const RpMesh    *mesh;

    RWFUNCTION(RWSTRING("_rpMeshWrite"));
    RWASSERT(meshHeader);
    RWASSERT(object);
    RWASSERT(stream);
    RWASSERT(matList);

    /* Write out a header */
    bmh.flags = meshHeader->flags;
    bmh.numMeshes = (RwUInt32)meshHeader->numMeshes;
    bmh.totalIndicesInMesh = meshHeader->totalIndicesInMesh;

    if (!RwStreamWriteInt32(stream, (const RwInt32 *)&bmh, sizeof(bmh)))
    {
        /* Failure */
        RWRETURN((RwStream *)NULL);
    }

    /* Then for each mesh... */
    mesh = (const RpMesh *)(meshHeader + 1);
    numMeshes = meshHeader->numMeshes;
    while (numMeshes--)
    {
        binMesh         bm;
        RwUInt32        numIndices;
        RxVertexIndex   *meshIndices;

        /* ... do another header... */
        bm.numIndices = mesh->numIndices;
        bm.matIndex = _rpMaterialListFindMaterialIndex(matList, mesh->material);

        /* Do something reasonably sensible on error conditions */
        if (bm.matIndex < 0)
        {
            bm.matIndex = 0;
        }

        if (!RwStreamWriteInt32(stream, (const RwInt32 *)&bm, sizeof(bm)))
        {
            /* Failure */
            RWRETURN((RwStream *)NULL);
        }

        if (((rpGEOMETRY == RwObjectGetType(object)) &&
             (!(rpGEOMETRYNATIVE &
                RpGeometryGetFlags((const RpGeometry *)object)))) ||
            ((rpWORLD == RwObjectGetType(object)) &&
             (!(rpWORLDNATIVE &
                RpWorldGetFlags((const RpWorld *)object)))))
        {
            /* ...and all the triangles */
            numIndices = mesh->numIndices;
            meshIndices = mesh->indices;

            while (numIndices > 0)
            {
                RpIndexBuffer   IndexBuffer;
                RwUInt32        writeIndices;
                RwUInt32        i;

                writeIndices = (numIndices < RWINDEXBUFFERSIZE) ?
                               (numIndices) : (RWINDEXBUFFERSIZE);

                for (i = 0; i < writeIndices; i++)
                {
                    IndexBuffer[i] = (RwUInt32)*meshIndices++;
                }

                if (!RwStreamWriteInt32(stream,
                                        (const RwInt32 *)&IndexBuffer[0],
                                        sizeof(IndexBuffer[0]) * writeIndices))
                {
                    /* Failure */
                    RWRETURN((RwStream *)NULL);
                }

                numIndices -= writeIndices;
            }
        }

        mesh++;
    }

    /* All done */
    RWRETURN(stream);
}

/****************************************************************************
 _rpMeshRead

 Reads a mesh from the given stream

 On entry   : Stream to read from
            : Object (geometry or world sector)
            : Material from object
 On exit    : Mesh read from the stream
 */
RpMeshHeader *
_rpMeshRead(RwStream *stream,
            const void *object,
            const RpMaterialList *matList)
{
    binMeshHeader   bmh;
    RpMeshHeader    *meshHeader;
    RwUInt32        size;

    RWFUNCTION(RWSTRING("_rpMeshRead"));
    RWASSERT(stream);
    RWASSERT(object);
    RWASSERT(matList);

    /* Read in a header */
    if (!RwStreamReadInt32(stream, (RwInt32 *)&bmh, sizeof(bmh)))
    {
        /* Failure */
        RWRETURN((RpMeshHeader *)NULL);
    }

    /* Figure out the size (little bit of slack for alignment) */
    size = (sizeof(RpMeshHeader) + ((sizeof(RpMesh) + 4) * bmh.numMeshes));

    if (((rpGEOMETRY == RwObjectGetType(object)) &&
         (!(rpGEOMETRYNATIVE &
            RpGeometryGetFlags((const RpGeometry *)object)))) ||
        ((rpWORLD == RwObjectGetType(object)) &&
         (!(rpWORLDNATIVE &
            RpWorldGetFlags((const RpWorld *)object)))))
    {
        size += (sizeof(RxVertexIndex) * bmh.totalIndicesInMesh);
    }

    /* Allocate memory for the mesh */
    meshHeader = _rpMeshHeaderCreate(size);
    if (meshHeader)
    {
        RpMesh          *mesh = (RpMesh *)(meshHeader + 1);
        RxVertexIndex   *meshIndices = (RxVertexIndex *)(mesh + bmh.numMeshes);
        RwUInt32        numMeshes;

        meshHeader->flags = bmh.flags;
        meshHeader->numMeshes = (RwUInt16)bmh.numMeshes;
        meshHeader->serialNum = RPMESHGLOBAL(nextSerialNum);
        meshHeader->totalIndicesInMesh = (RwUInt32)bmh.totalIndicesInMesh;
        meshHeader->firstMeshOffset = 0;

        RPMESHGLOBAL(nextSerialNum)++;

        /* Then for each mesh... */
        numMeshes = meshHeader->numMeshes;
        while (numMeshes-- > 0)
        {
            binMesh bm;

            /* ... do another header... */
            if (NULL == RwStreamReadInt32(stream, (RwInt32 *)&bm, sizeof(bm)))
            {
                /* Failure */
                RWRETURN((RpMeshHeader *)NULL);
            }

            mesh->numIndices = bm.numIndices;
            mesh->material = _rpMaterialListGetMaterial(matList, bm.matIndex);
            mesh->indices = meshIndices;

            if (((rpGEOMETRY == RwObjectGetType(object)) &&
                 (!(rpGEOMETRYNATIVE &
                    RpGeometryGetFlags((const RpGeometry *)object)))) ||
                ((rpWORLD == RwObjectGetType(object)) &&
                 (!(rpWORLDNATIVE &
                    RpWorldGetFlags((const RpWorld *)object)))))
            {
                RwUInt32    remainingIndices;

                /* ...and all the indices */
                remainingIndices = mesh->numIndices;
                while (remainingIndices > 0)
                {
                    RpIndexBuffer   IndexBuffer;
                    RwInt32         *source = (RwInt32 *) & IndexBuffer[0];
                    RwUInt32        readIndices;

                    readIndices = (remainingIndices < RWINDEXBUFFERSIZE) ?
                                  (remainingIndices) : (RWINDEXBUFFERSIZE);

                    if (!RwStreamReadInt32(stream, source,
                                           sizeof(RwInt32) * readIndices))
                    {
                        /* Failure */
                        RWRETURN((RpMeshHeader *)NULL);
                    }

                    remainingIndices -= readIndices;
                    while (readIndices-- > 0)
                    {
                        *meshIndices++ = (RxVertexIndex)(*source++);
                    }
                }
            }

            mesh++;
        }
    }

    /* All done */
    RWRETURN(meshHeader);
}

/****************************************************************************
 _rpMeshSize

 Calculates size of mesh (in bytes) when serialised

 On entry   : Mesh to size up
            : Object the mesh belongs to
 On exit    : Size of mesh when serialised (in bytes)
 */
RwInt32
_rpMeshSize(const RpMeshHeader *meshHeader,
            const void *object)
{
    RwUInt32    size;

    RWFUNCTION(RWSTRING("_rpMeshSize"));
    RWASSERT(meshHeader);

    if (((rpGEOMETRY == RwObjectGetType(object)) &&
         ((rpGEOMETRYNATIVE &
           RpGeometryGetFlags((const RpGeometry *)object)))) ||
        ((rpWORLD == RwObjectGetType(object)) &&
         ((rpWORLDNATIVE &
           RpWorldGetFlags((const RpWorld *)object)))))
    {
        /* The binary size is pretty easy */
        size = (sizeof(binMeshHeader) +
               (meshHeader->numMeshes * sizeof(binMesh)));
    }
    else
    {
        /* The binary size is pretty easy */
        size = (sizeof(binMeshHeader) +
               (meshHeader->numMeshes * sizeof(binMesh)) +
               (meshHeader->totalIndicesInMesh * sizeof(RwUInt32)));
    }

    RWRETURN(size);
}

RwInt16
_rpMeshGetNextSerialNumber(void)
{
    RwInt16 serialNum;

    RWFUNCTION(RWSTRING("_rpMeshGetNextSerialNumber"));

    serialNum = RPMESHGLOBAL(nextSerialNum);

    RPMESHGLOBAL(nextSerialNum)++;

    RWRETURN(serialNum);
}
